#include <pthread.h>
#include <iostream>

#include "ServerSideSynchronizer.h"
#include "SnakePlayer.h"
#include "MyMap.h"
#include "game-printer.h"

#define SERVER_PLAYER_NUMBER 0
#define SERVER_TIME_SEND 200000
ServerSideSynchronizer * ServerSideSynchronizer::instance= NULL;
std::string * ServerSideSynchronizer::serializePlayerMoves(PlayerMoves *moves)
{
	std::string * to_ret= NULL;

	return to_ret;
}

ServerSideSynchronizer::ServerSideSynchronizer()
{

	pthread_mutex_init(&write_to_shared_mutex, NULL);
	pthread_mutex_init(&allow_sending_mutex, NULL);
	shared_thread_memory =(SharedMemStruct*) calloc(1,
			sizeof(SharedMemStruct));

	shared_thread_memory->server_round_number = 0;
	resetSharedMemory(true);

	s.udpServerConfig();
	s.init();
}

void ServerSideSynchronizer::lockSharedMemory()
{
	pthread_mutex_lock( &write_to_shared_mutex);
}

void ServerSideSynchronizer::unlockSharedMemory()
{
	pthread_mutex_unlock( &write_to_shared_mutex);
}

ServerSideSynchronizer * ServerSideSynchronizer::getInstance()
{
	if (instance == NULL)
	{
		instance = new ServerSideSynchronizer();
	}
	return instance;
}

/* implementing from PlayerSynchronizer*/
void ServerSideSynchronizer::synchronize()
{
#ifdef H_DEBUG_ALL
	printf("Synchronizing....");
#endif
	//increment round number
	int new_round_number = MyMap::getInstance()->incrementRoundNumber();

	setServerRoundNumber(new_round_number);

	/* moving */
	for (int i = 0; i < shared_thread_memory->current_players_number; ++i)
	{

		movePlayerOnMap(i);
	}

	int winner_id = MyMap::instanceOf()->getWinPlayerNumber();

	/* DISCOVER GAME OVER PLAYER*/
	for (int i = 0; i < shared_thread_memory->current_players_number; ++i)
	{
		if ( true == (MyMap::getInstance()->getSnakePlayerByNumber(i))->getGameOver() )
		{
			informPlayerGameOver(i);

		}
	}

	if (MyMap::snake_winner_all_lost == winner_id)
	{
		/* new package created - send new buffers*/
		allowSendingNewPackage();
		/* everybody lost - not incrementing wins for players*/
#ifdef H_DEBUG_ALL		
		printf("ALL HAVE LOST! Restart!!!!");
#endif		
		sleep(1);
		restartGame();
		allowSendingNewPackage();
		sleep(1);
		startGame();
	}
	else if (MyMap::snake_winner_none != winner_id)
	{
		/* somebody won round of game */
		informPlayerGameWin(winner_id);
		/* new package created - send new buffers*/
		allowSendingNewPackage();
#ifdef H_DEBUG_ALL
		printf("Restart!!!!");
#endif		
		sleep(1);
		restartGame();
		allowSendingNewPackage();
		sleep(1);
		startGame();
	}

	/* new package created */
	allowSendingNewPackage();
#ifdef H_DEBUG_ALL
	printf("Filling player_moves structs");
#endif	
	fillPlayerMovesStruct();
#ifdef H_DEBUG_ALL	
	printf("Done;p");
#endif	
}

SharedMemStruct * ServerSideSynchronizer::getSharedMemoryPtr()
{
	return shared_thread_memory;
}

int ServerSideSynchronizer::getPlayersNumber()
{
	return shared_thread_memory->current_players_number;
}

void ServerSideSynchronizer::startThreads()
{
	pthread_t sender_thread, resolver_thread, game_thread;

	int iret1, iret2, iret3;

	/* Create independent threads each of which will execute function */

	iret1 = pthread_create( &sender_thread, NULL, sender_func,
			(void*) ServerSideSynchronizer::getInstance() );
	iret2 = pthread_create( &resolver_thread, NULL, resolver_func,
			(void*) ServerSideSynchronizer::getInstance());

	iret3 = pthread_create( &game_thread, NULL, game_func,
			(void*) ServerSideSynchronizer::getInstance());
	/* Wait till threads are complete before main continues. Unless we  */
	/* wait we run the risk of executing an exit which will terminate   */
	/* the process and all threads before the threads have completed.   */

	pthread_join(sender_thread, NULL);
	pthread_join(resolver_thread, NULL);
	pthread_join(game_thread, NULL);

	printf("Thread 1 returns: %d\n", iret1);
	printf("Thread 2 returns: %d\n", iret2);
	exit(0);
}

void ServerSideSynchronizer::movePlayerOnMap(int player_number)
{
	lockSharedMemory();
	Direction
			dir =
					shared_thread_memory->last_client_direction[ player_number ];
	unlockSharedMemory();

	addPlayerMove(player_number, dir);
	SnakePlayer* player = MyMap::instanceOf()->getSnakePlayerByNumber(player_number);

	assert(player);
	if (player != NULL)
	{
		player->moveTo(dir);
	}
	else
	{
		printf("Player #%d not initialized!!!!!", player_number);
	}
}

void ServerSideSynchronizer::addPlayerMove(int player_number, Direction & dir)
{
	lockSharedMemory();
	/* move all directions to right one position -direction[0] will be now free*/
	for (int i= PLAYER_MAX_MOVES_NUMBER -1; i > 0; --i)
	{
		assert(0 != i);
		shared_thread_memory->player_moves[player_number].last_directions[i]
				= shared_thread_memory->player_moves[player_number].last_directions[i -1];
	}

	/* set actual direction*/
	shared_thread_memory->player_moves[player_number].last_directions[0]
			= dir;
	unlockSharedMemory();
}

int ServerSideSynchronizer::generateAndInitializePlayer()
{

	lockSharedMemory();

	int player_number = shared_thread_memory->current_players_number;
	SnakePlayer * new_snake_player =new SnakePlayer(player_number);

	assert(new_snake_player);
	shared_thread_memory->player_active_state[player_number]
			= PLAYER_STATE_ACTIVE;
	//increment player number
	shared_thread_memory->current_players_number++;
	unlockSharedMemory();
	assert((shared_thread_memory->current_players_number) <= PLAYERS_MAX_AMOUNT);

	//filling player moves
	fillPlayerMovesStruct();
#ifdef H_DEBUG_ALL	
	printf("PLAYER INITIALIZED.... NUMBER OF PLAYERS %d \n",
			shared_thread_memory->current_players_number);
#endif	
	return player_number;
}

void ServerSideSynchronizer::resetSharedMemory(bool flush_ips)
{
	lockSharedMemory();
	shared_thread_memory->game_started = STATE_GAME_STOP;
	shared_thread_memory->server_round_number = 0;

	if (true == flush_ips)
	{
		shared_thread_memory->game_number = 0;
		shared_thread_memory->current_players_number = 0;

		for (int i =0; i < PLAYERS_MAX_AMOUNT; ++i)
		{
			shared_thread_memory->player_active_state[i]
					= PLAYER_STATE_DISABLED;
			resetPlayerMoves(shared_thread_memory->player_moves[i]);
			//player wins amount
			shared_thread_memory->player_wins[i] = 0;
		}
	}

	for (int i =0; i < PLAYERS_MAX_AMOUNT; ++i)
	{

		resetPlayerMoves(shared_thread_memory->player_moves[i]);
	}
	unlockSharedMemory();

	fillPlayerMovesStruct();
}

void ServerSideSynchronizer::setClientLastDirection(
						    int player_number,
						    Direction & dir)
{
	/*
	 printf("IN FUNC NUMBER OF PLAYERS %d\n",
	 shared_thread_memory->current_players_number);
	 */
	assert(player_number < shared_thread_memory->current_players_number);
	lockSharedMemory();

	shared_thread_memory->last_client_direction[player_number] = dir;
	unlockSharedMemory();
}

Direction & ServerSideSynchronizer::getClientLastDirection(int player_number)
{
	return shared_thread_memory->last_client_direction[player_number];
}

void ServerSideSynchronizer::setServerRoundNumber(int number)
{
	lockSharedMemory();
	shared_thread_memory->server_round_number = number;
	unlockSharedMemory();
}

void ServerSideSynchronizer::startGame()
{
	lockSharedMemory();
	shared_thread_memory->game_started = STATE_GAME_STARTED;

	//sending notification
	//SharedMemStruct test_shar;
	//test_shar.game_started = STATE_GAME_STARTED;
#ifdef H_DEBUG_ALL
	printf("\n\n\n GAME START!!!!\n\n");
	printDebug();
#endif	
	s.send((char *) shared_thread_memory, sizeof(SharedMemStruct));

	//s.send((char *) shared_thread_memory, sizeof(shared_thread_memory) );
	unlockSharedMemory();
	//printf("sending: gs #%d\n",shared_thread_memory->game_started);
}

void ServerSideSynchronizer::printDebug()
{
	std::string debug = "";
	debug = MyMap::getInstance()->serialize();

	std::cout << "*\n******************************\n";
	std::cout << "Number of Game:: " << shared_thread_memory->game_number
			<< "\n";
	std::cout << "Game round:: "
			<< shared_thread_memory->server_round_number << "\n";
	std::cout << "Map round:: " << MyMap::getInstance()->getRoundNumber() << "\n";
	std::cout << "Number of players:: "
			<< shared_thread_memory->current_players_number << "\n";
	std::cout << "Game started state:: "
			<< shared_thread_memory->game_started << "\n";
	std::cout << "Player 0 wins:: " << shared_thread_memory->player_wins[0]
			<< "\n";
	std::cout << "Player 1 wins:: " << shared_thread_memory->player_wins[1]
			<< "\n";
	std::cout << debug;

	for (int i = 0; i < shared_thread_memory->current_players_number; ++i)
	{
		cout
				<< debugPlayerMoves( &shared_thread_memory->player_moves[i]);
	}
}

void ServerSideSynchronizer::informPlayerGameOver(int player_number)
{
#ifdef H_DEBUG_ALL
	printf("\n\n!!!Player #%d game over \n", player_number);
#endif
	lockSharedMemory();
	(shared_thread_memory->player_moves[player_number]).active_state
			= PLAYER_STATE_GAME_LOST;
	unlockSharedMemory();

	if (player_number == SERVER_PLAYER_NUMBER)
	{
		//gp_inform_game_over( getAppData() );
		gp_inform_lose_win(
				getAppData(),
				false,
				shared_thread_memory->player_wins[SERVER_PLAYER_NUMBER] ,
				shared_thread_memory->player_wins[SERVER_PLAYER_NUMBER+1] + 1,/* hach to show correct win value */
				s.udpPlayers_list[0].nick);
	}
}

void ServerSideSynchronizer::informPlayerGameWin(int player_number)
{
#ifdef H_DEBUG_ALL
	printf("\n\n\nPlayer #%d WIN \n\n\n\n", player_number);

	printf("PLAYER WON +1");
#endif	
	//cout << "PLAYER " << player_number << " win" <<endl;
	lockSharedMemory();
	shared_thread_memory->player_wins[ player_number ]
			=shared_thread_memory->player_wins[ player_number ] + 1;

	(shared_thread_memory->player_moves[player_number]).active_state
			= PLAYER_STATE_GAME_WIN;
	unlockSharedMemory();

	if (player_number == SERVER_PLAYER_NUMBER)
	{
		//gp_inform_game_over( getAppData() );
		gp_inform_lose_win(
				getAppData(),
				true,
				shared_thread_memory->player_wins[SERVER_PLAYER_NUMBER] ,
				shared_thread_memory->player_wins[SERVER_PLAYER_NUMBER+1],
				s.udpPlayers_list[0].nick);
	}
}

void ServerSideSynchronizer::restartGame()
{
	MyMap::getInstance()->resetMapAndSnakes();
	MyMap::getInstance()->markAllChanged();
	/* reset shared threads struct but without touching players data*/
	resetSharedMemory(false);
	//increment number of games
	shared_thread_memory->game_number++;
	shared_thread_memory->game_started = STATE_GAME_STOP;

}
void ServerSideSynchronizer::sendToAllPlayers()
{
	s.send((char *) shared_thread_memory, sizeof(SharedMemStruct));
}
void ServerSideSynchronizer::allowSendingNewPackage()
{
	// unlocks mutex, allows sending new package
	pthread_mutex_unlock( &allow_sending_mutex);
}
void ServerSideSynchronizer::waitForNewPackage()
{
	//locks mutex, stops sending thread
	pthread_mutex_lock( &allow_sending_mutex);
}

void ServerSideSynchronizer::fillPlayerMovesStruct()
{

	SnakePlayer* snake_t= NULL;

	lockSharedMemory();
	for (int i = 0; i < shared_thread_memory->current_players_number; ++i)
	{
		//player_moves_t = & (shared_thread_memory->player_moves[i] );
		snake_t = MyMap::getInstance()->getSnakePlayerByNumber(i);

		assert( snake_t );
		(shared_thread_memory->player_moves[i] ).actual_x
				= snake_t->getHeadSegment()->x;
		(shared_thread_memory->player_moves[i] ).actual_y
				= snake_t->getHeadSegment()->y;
		(shared_thread_memory->player_moves[i] ).player_number
				= snake_t->getNumber();
		(shared_thread_memory->player_moves[i] ).remote_round_number
				= shared_thread_memory->server_round_number;

	}
	unlockSharedMemory();

}
int ServerSideSynchronizer::processPlayersRegistration()
{
	generateAndInitializePlayer();
	if (0 == s.addUdpPlayers() )
	{
		generateAndInitializePlayer();
		return 0;
	}
	return 1;
}
int ServerSideSynchronizer::getClientUdpNumber(int number)
{
	assert(number> 0);
	return number -1;
}

int ServerSideSynchronizer::resolvePlayerData()
{
	int server_num = 1;
	int udp_num = getClientUdpNumber(server_num);
	int ret_val = s.receiveUdpPlayersMoves(2121);
	setClientLastDirection(server_num, (s.udpPlayers_list[udp_num]).last_dir);
	
	return ret_val;

}

int ServerSideSynchronizer::getPlayerWinLostStatus(int number)
{
	lockSharedMemory();

	int to_ret = shared_thread_memory->player_moves[number].active_state;
	unlockSharedMemory();
	return to_ret;
}
/* ###############THREADS DEFINITIONS*/

void *game_func(void *ptr)
{
	ServerSideSynchronizer * synchronizer = (ServerSideSynchronizer *) ptr;

	gp_do_test_gaming(synchronizer->getAppData());
}

void *resolver_func(void *ptr)
{

	ServerSideSynchronizer * synchronizer = (ServerSideSynchronizer *) ptr;

	//int test_player2_number = 0;
	while (1)
	{

		if (-11 == synchronizer->resolvePlayerData() )
		{
			cout << "KLIENT SIE ROZLACZYL " <<endl;
			exit(1);
		}

	}
}

void *sender_func(void *ptr)
{

	ServerSideSynchronizer * synchronizer = (ServerSideSynchronizer *) ptr;
	while (1)
	{

		synchronizer->sendToAllPlayers();
		/* sending time simulation */
		//usleep( SERVER_TIME_SEND );
		//synchronizer->sendToAllPlayers();
		//}
		synchronizer->waitForNewPackage();
	}

}

